//go:build windows
// +build windows

// TOdo 按 github.com/lxn/win/user32.go 重新整理以下
package win窗口类

import (
	时间类 "gitee.com/go_888/extends/carbon"
	扩展类 "gitee.com/go_888/extends/extend"
	文本类 "gitee.com/go_888/extends/goframe/gstr"
	内部类2 "gitee.com/go_888/extends/windows/internal/windows"
	进程类 "gitee.com/go_888/extends/windows/process"
	"gitee.com/go_888/internal-call/win"
	"sync"
	"syscall"
	"unsafe"
)

// X写字 在指定窗口上绘制文本
// 参数:
// 句柄: 目标窗口的句柄，如果为0则使用前台窗口
// 内容: 要绘制的文本内容
// 字体名称: 使用的字体名称，默认为"宋体"
// 字体大小: 字体大小，默认为26
// 字体颜色: 文本颜色，使用RGB值，默认为红色(255)
// x, y: 文本绘制的起始坐标
func X写字(句柄 int, 内容, 字体名称 string, 字体大小, 字体颜色, x, y int) {
	var hdc win.HDC
	// 如果句柄为0，则获取前台窗口句柄
	if 句柄 == 0 {
		句柄 = int(win.GetForegroundWindow())
	}

	// 获取设备上下文句柄
	hdc = win.GetDC(win.HWND(句柄))
	if hdc == 0 {
		return
	}
	// 函数结束时释放设备上下文
	defer win.ReleaseDC(win.HWND(句柄), hdc)

	// 设置默认文本内容
	if 内容 == "" {
		内容 = "精易论坛 bbs.125.la"
	}
	// 设置默认字体名称
	if 字体名称 == "" {
		字体名称 = "宋体"
	}
	// 设置默认字体大小
	if 字体大小 == 0 {
		字体大小 = 26
	}
	// 设置默认字体颜色(红色)
	if 字体颜色 == 0 {
		字体颜色 = 255 // red
	}

	// 创建字体结构体并设置属性
	var logFont win.LOGFONT
	logFont.LfCharSet = win.DEFAULT_CHARSET     // 默认字符集
	logFont.LfHeight = int32(字体大小)              // 字体高度
	logFont.LfQuality = win.ANTIALIASED_QUALITY // 抗锯齿质量
	logFont.LfWeight = win.FW_NORMAL            // 正常字重

	// 将字体名称转换为UTF-16格式并复制到结构体
	fontNameUTF16, err := syscall.UTF16FromString(字体名称)
	if err != nil {
		return
	}
	copy(logFont.LfFaceName[:], fontNameUTF16)

	// 创建字体对象
	hFont := win.CreateFontIndirect(&logFont)
	if hFont == 0 {
		return
	}
	// 函数结束时删除字体对象
	defer win.DeleteObject(win.HGDIOBJ(hFont))

	// 设置设备上下文属性
	oldFont := win.SelectObject(hdc, win.HGDIOBJ(hFont))      // 选择字体
	oldBkMode := win.SetBkMode(hdc, win.TRANSPARENT)          // 设置背景透明
	oldTextColor := win.SetTextColor(hdc, win.COLORREF(字体颜色)) // 设置文本颜色

	// 将文本内容转换为UTF-16格式
	contentUTF16, err := syscall.UTF16FromString(内容)
	if err != nil {
		return
	}
	// 在指定位置绘制文本
	win.TextOut(hdc, int32(x), int32(y), &contentUTF16[0], int32(len(contentUTF16)-1))

	// 恢复设备上下文的原始设置
	win.SetBkMode(hdc, oldBkMode)
	win.SetTextColor(hdc, oldTextColor)
	win.SelectObject(hdc, oldFont)
}

// X是否响应 通过发送消息给指定窗口来检查窗口是否响应。
// 参数:
// 窗口句柄: 目标窗口的句柄。
// 返回值:
// 如果窗口响应，则返回true；否则返回false。
func X是否响应(窗口句柄 int) bool {
	// 使用user32.X库的调用命令功能发送消息给窗口。
	// 参数解释：
	//   "sendMessageTimeoutA": 发送消息并等待响应的API函数名。
	//   uintptr(窗口句柄): 目标窗口的句柄，转换为uintptr类型。
	//   0, 0, 0: 消息的相关参数，在此场景下不关心具体消息内容。
	//   uintptr(2): 消息发送模式，2表示SMTO_NORMAL模式，即正常阻塞模式。
	//   uintptr(128): 超时时间，128毫秒，超过该时间后函数调用将返回。
	//   0: 未使用参数。
	R1, _, _ := sendMessageTimeoutA.X调用(uintptr(窗口句柄), 0, 0, 0, uintptr(2), uintptr(128), 0)

	// 如果结果.R1不等于0，表示窗口是响应的。
	if int(R1) != 0 {
		return true
	} else {
		return false
	}
}

// X取光标坐标 返回当前光标在窗口中的坐标。
// 该函数通过调用Windows API GetCursorPos获取光标位置。
// 返回值x, y表示光标当前位置的横纵坐标。
func X取光标坐标() (x, y int) {
	var pt win.POINT
	win.GetCursorPos(&pt)
	// 返回光标的横纵坐标。
	return int(pt.X), int(pt.Y)
}

func X取活动窗口() int {
	return int(win.GetForegroundWindow())
}

// X置活动窗口 将指定的窗口标记为“活动窗口”（Active Window），并使其成为前台窗口（Foreground Window）。
// 活动窗口通常是用户当前正在交互的窗口，它会接收键盘输入和其他用户操作。
// 窗口句柄是用于标识窗口的唯一数字。
// 返回错误如果激活过程中发生问题。
func X置活动窗口(窗口句柄 int) error {
	// 定义变量用于存储当前线程ID和目标窗口的线程ID。
	var 局_当前线程ID, 局_目标线程ID uintptr

	// 获取当前线程ID
	局_当前线程ID, _, _ = getCurrentThreadId.X调用()

	// 获取目标窗口的线程ID
	局_目标线程ID, _, _ = getWindowThreadProcessId.X调用(uintptr(窗口句柄), 0)

	// 附加线程输入，使得当前线程可以控制目标窗口的输入设备。
	attachThreadInput.X调用(局_目标线程ID, 局_当前线程ID, 1)

	// 设置活动窗口
	setActiveWindow.X调用(uintptr(窗口句柄))

	// 分离线程输入，恢复之前的线程输入状态。
	attachThreadInput.X调用(局_目标线程ID, 局_当前线程ID, 0)

	// 如果没有发生任何错误，返回nil。
	return nil
}

// X还原 通过窗口句柄将窗口从最小化状态恢复至正常状态。
// 参数:
// 句柄 - 目标窗口的句柄。
// 返回值:
// 如果窗口成功还原，则返回 true；否则返回 false。
func X还原(句柄 int32) bool {
	// 调用 Windows API 发送窗口还原（OpenIcon）命令。
	R1, _, _ := openIcon.X调用(uintptr(句柄))

	// 检查窗口还原操作是否成功。
	if R1 == 0 {
		// 如果 R1 为 0，表示窗口还原失败。
		return false
	} else {
		// 如果 R1 非 0，表示窗口还原成功。
		return true
	}
}

// X置顶 将指定的窗口置顶，使其始终位于其他窗口之上。
// 窗口句柄是标识窗口的整数标识符。
func X置顶(窗口句柄 int, 开启 bool) {
	if 开启 {
		win.SetWindowPos(win.HWND(窗口句柄), win.HWND_TOPMOST, 0, 0, 0, 0, win.SWP_NOMOVE|win.SWP_NOSIZE)
	} else {
		win.SetWindowPos(win.HWND(窗口句柄), win.HWND_NOTOPMOST, 0, 0, 0, 0, win.SWP_NOMOVE|win.SWP_NOSIZE)
	}
	X置前台窗口(窗口句柄)
}

// X关闭 通过发送关闭消息来关闭指定的窗口。
// 参数 窗口句柄: 目标窗口的句柄。
func X关闭(窗口句柄 int) {
	// 通过调用Windows API PostMessageW函数，发送WM_CLOSE消息来关闭窗口。
	postMessageW.X调用(uintptr(窗口句柄), 16, 0, 0)
}

func X置父(子窗口句柄, 父窗口句柄 int) {
	hwnd := win.HWND(子窗口句柄)
	//给窗口添加 WS_EX_NOACTIVATE 样式, 防止窗口被激活导致父窗口失去焦点
	//currentStyle := win.GetWindowLong(hwnd, win.GWL_EXSTYLE) // 获取当前窗口扩展样式
	//newStyle := currentStyle | win.WS_EX_NOACTIVATE          // 添加 WS_EX_NOACTIVATE 样式
	//win.SetWindowLong(hwnd, win.GWL_EXSTYLE, newStyle)       // 设置新的窗口扩展样式
	win.SetParent(hwnd, win.HWND(父窗口句柄))
}

func X置前台窗口(窗口句柄 int) bool {
	return win.SetForegroundWindow(win.HWND(窗口句柄))
}

// X取标题 通过窗口句柄获取窗口的标题。
// 参数:
// 窗口句柄 - 窗口的句柄。
// 返回值:
// 窗口的标题字符串。
func X取标题(窗口句柄 int) string {
	return 内部类2.X取标题(窗口句柄)
}

// X置标题 设置窗口的标题。
// 该函数通过窗口句柄设置新标题，使用UTF-16编码通过SetWindowTextW函数实现。
// 参数:
// 窗口句柄 - 窗口的句柄。
// 新标题 - 要设置的新标题。
// 返回值:
// 如果成功设置标题，返回true；否则返回false。
func X置标题(窗口句柄 int, 新标题 string) bool {
	// 定义 HWND 类型，用于窗口句柄。
	type HWND uintptr

	// 调用SetWindowTextW函数，尝试设置窗口标题。
	// 参数分别为窗口句柄和标题的UTF-16编码指针。
	r1, _, err := setWindowTextW.X调用(uintptr(窗口句柄), 内部类2.DLL_go文本到UTF16文本_LPCWSTR(新标题))

	// 检查返回值和错误，判断标题是否成功设置。
	// 如果返回值不为0或没有错误，则认为标题设置成功。
	if r1 != 0 || err == nil {
		return true
	}
	return false
}

// X取类名 通过窗口句柄检索并返回窗口类名。
// 窗口句柄: 指定窗口的句柄。
// 返回值: 返回窗口的类名字符串；如果无法获取类名，则返回空字符串。
func X取类名(窗口句柄 int) string {
	return 内部类2.X取类名(窗口句柄)
}

// X置状态 设置指定窗口的状态
// 该函数通过调用Windows API的ShowWindow函数来改变窗口的状态。
// 参数:
// 窗口句柄: 目标窗口的句柄。
// 状态类型:
// 0 隐藏取消激活
// 1 激活并显示窗口
// 2 最小化激活
// 3 最大化激活
// 4 还原(以最近的大小和位置显示窗口)
// 5 激活窗口并以当前大小和位置显示窗口
// 7 最小化
// 9 激活并显示窗口,如果窗口最小化、最大化或排列，系统会将其还原到其原始大小和位置。 还原最小化窗口时，应用程序应指定此标志。
//
// 返回值:
// 成功设置窗口状态则返回true，否则返回false。
func X置状态(窗口句柄, 状态类型 int) bool {
	return win.ShowWindow(win.HWND(窗口句柄), int32(状态类型))
}
func X最大化(窗口句柄 int) bool {
	return X置状态(窗口句柄, 3)
}

func X最小化(窗口句柄 int) bool {
	return X置状态(窗口句柄, 7)
}

// X取祖句柄 通过调用Windows API获取指定窗口的祖先句柄。
// 窗口句柄是用于标识窗口的整数型句柄。
// 返回值是祖先窗口的句柄，如果获取失败则返回0。
func X取祖句柄(窗口句柄 int) int {
	return int(win.GetAncestor(win.HWND(窗口句柄), 3))
}

// X取父句柄 通过指定的窗口句柄获取其父窗口的句柄。
// 参数 窗口句柄: 需要获取父窗口句柄的窗口句柄。
// 返回值为父窗口的句柄，如果获取失败则返回0。
func X取父句柄(窗口句柄 int) int {
	return int(win.GetParent(win.HWND(窗口句柄)))
}

// X是否可见 检查指定的窗口是否可见。
// 可见窗口是可以被用户看到的，不一定处于前台或获得焦点。
// 参数:
// 窗口句柄: 表示要检查的窗口的句柄。
// 返回值:
// 返回true表示窗口可见，返回false表示窗口不可见。
func X是否可见(窗口句柄 int) bool {
	return win.IsWindowVisible(win.HWND(窗口句柄))
}

// X显示隐藏 是一个用于控制窗口显示或隐藏的函数。
// 它通过窗口句柄来识别特定的窗口，并根据显示参数决定窗口的状态。
// 如果显示参数为true，窗口将显示出来；如果为false，窗口将被隐藏。
// 函数返回一个bool值，指示操作是否成功。
func X显示隐藏(窗口句柄 int, 显示 bool) bool {
	// 使用user32.X调用命令来执行ShowWindowAsync操作，传递窗口句柄和显示参数。
	// DLL_go布尔值转换_BOOL 是一个帮助函数，用于将布尔值转换为函数调用所需的指针类型。
	结果, _, _ := showWindowAsync.X调用(uintptr(窗口句柄), 内部类2.DLL_go布尔值转换_BOOL(显示))
	// 检查操作结果是否为0，非0表示调用成功。
	if 结果 == 0 {
		// 操作失败，返回false。
		return false
	} else {
		// 操作成功，返回true。
		return true
	}
}

func X禁用(窗口句柄 int, 禁用 bool) bool {
	return win.EnableWindow(win.HWND(窗口句柄), !禁用)
}

// X取桌面句柄 通过调用Windows API获取桌面句柄。
// 返回值是桌面句柄的整数表示。如果句柄获取失败，返回0。
func X取桌面句柄() int {
	return int(win.GetDesktopWindow())
}

// X取控件客户区大小 获取指定窗口的客户区的宽度和高度。
// 窗口句柄是标识窗口的整数。
// 返回值宽度和高度分别代表窗口客户区的宽度和高度。
func X取窗口客户区大小(窗口句柄 int) (宽度, 高度 int, 是否成功 bool) {
	var pt win.RECT
	是否成功 = win.GetClientRect(win.HWND(窗口句柄), &pt)

	// 计算窗口客户区的宽度和高度。
	窗口客户区宽度 := int(pt.Right) - int(pt.Left)
	窗口客户区高度 := int(pt.Bottom) - int(pt.Top)

	// 如果getClientRect函数失败，则返回0, 0。
	if 是否成功 == false {
		return 0, 0, false
	}
	return 窗口客户区宽度, 窗口客户区高度, 是否成功
}

// X取焦点句柄 获取当前窗口的焦点句柄
// 返回值为焦点句柄的整型表示，如果获取失败则返回0
func X取焦点句柄() int {
	return int(win.GetForegroundWindow())
}

// X置控件焦点 设置指定窗口的焦点。
// 和窗口置焦点()不同, 此函数会强制设置焦点同时激活窗口，而窗口置焦点()只会设置焦点，但不会激活窗口。
// 参数:
// hwnd: 目标窗口的句柄。
// 返回值:
// 如果成功设置焦点，则返回true；否则返回false。
func X置控件焦点(hwnd int) bool {
	// 获取当前线程ID
	currentThreadID := win.GetCurrentThreadId()

	// 获取目标窗口线程ID
	var processID uint32
	targetThreadID := win.GetWindowThreadProcessId(win.HWND(hwnd), &processID)

	// 如果线程ID相同，直接设置焦点
	if targetThreadID == currentThreadID {
		return win.SetFocus(win.HWND(hwnd)) != 0
	}

	// 线程ID不同，需要附加线程输入队列
	if !win.AttachThreadInput(int32(targetThreadID), int32(currentThreadID), true) {
		return false
	}

	// 设置焦点
	result := win.SetFocus(win.HWND(hwnd)) != 0

	// 分离线程输入队列
	win.AttachThreadInput(int32(targetThreadID), int32(currentThreadID), false)

	return result
}

// X是否最小化 通过窗口句柄检查窗口是否被最小化。
// 参数 窗口句柄: 用于标识要检查的窗口。
// 返回值: 如果窗口被最小化，则返回true；否则返回false。
func X是否最小化(窗口句柄 int) bool {
	return win.IsIconic(win.HWND(窗口句柄))
}

// X是否最大化 检查给定窗口句柄的窗口是否已被最大化。
// 通过调用isZoomed.X调用命令方法，并传入窗口句柄，来查询窗口的状态。
// 返回值为布尔类型，非0表示窗口已被最大化。
func X是否最大化(窗口句柄 int) bool {
	return win.IsZoomed(win.HWND(窗口句柄))
}

// X取控件内容 通过窗口句柄获取控件的内容字符串。
// 参数:
// 窗口句柄 - 目标窗口的句柄。
// 返回值:
// 控件的内容字符串。如果获取失败或句柄无效，则返回空字符串。
func X取控件内容(窗口句柄 int) string {
	//备注2025-2-23, 已确定可以用, 并且没有bug
	// 获取控件文本的长度
	textLength := win.SendMessage(win.HWND(窗口句柄), win.WM_GETTEXTLENGTH, 0, 0)
	if textLength == 0 {
		return ""
	}

	// 分配缓冲区，大小为 textLength + 1（包括 null 终止符）
	buffer := make([]uint16, textLength+1)

	// 发送 WM_GETTEXT 消息获取控件文本
	win.SendMessage(win.HWND(窗口句柄), win.WM_GETTEXT, textLength+1, uintptr(unsafe.Pointer(&buffer[0])))

	// 将 UTF-16 编码的文本转换为 Go 的字符串
	return syscall.UTF16ToString(buffer)
}

// X取控件内容 通过窗口句柄获取控件的内容字符串。
// 参数:
// 窗口句柄 - 目标窗口的句柄。
// 返回值:
// 控件的内容字符串。如果获取失败或句柄无效，则返回空字符串。
func X置控件内容(窗口句柄 int, 内容 string) bool {
	//备注2025-2-23, 已确定可以用, 并且没有bug
	utf16Text, err := syscall.UTF16FromString(内容)
	if err != nil {
		panic(err)
	}
	result := win.SendMessage(win.HWND(窗口句柄), win.WM_SETTEXT, 0, uintptr(unsafe.Pointer(&utf16Text[0])))
	if result != 1 {
		return false
	}
	return true
}

// X取控件内容 通过窗口句柄获取控件的内容字符串。
// 参数:
// 窗口句柄 - 目标窗口的句柄。
// 返回值:
// 控件的内容字符串。如果获取失败或句柄无效，则返回空字符串。
func X按钮单击(按钮句柄 int) bool {
	// 发送 BM_CLICK 消息，模拟按钮单击
	result := win.SendMessage(win.HWND(按钮句柄), win.BM_CLICK, 0, 0)
	if result == 0 {
		return false
	} else {
		return true
	}
}

// X枚举顶级窗口 用于枚举当前系统中的所有窗口信息。
// 该函数通过系统调用获取每个窗口的句柄，并为每个窗口创建信息对象，
// 包含窗口的句柄、标题、类名、进程ID和线程ID。
func X枚举顶级窗口() []X窗口信息 {
	// windows 用于存储所有窗口的信息。
	var windows []X窗口信息
	// mu 用于确保并发访问 windows 时的数据安全。
	var mu sync.Mutex

	// 创建枚举窗口的回调函数
	callback := func(hwnd uintptr, lParam uintptr) uintptr {
		mu.Lock()
		defer mu.Unlock()

		// 将窗口句柄添加到 windows 切片中
		windows = append(windows, X窗口信息{
			X窗口句柄: int(hwnd),
		})
		return 1
	}

	// 调用 enumWindows 函数开始枚举窗口
	_, _, _ = enumWindows.X调用(uintptr(unsafe.Pointer(syscall.NewCallback(callback))), 0)

	// 使用 goroutines 处理每个窗口的信息
	var wg sync.WaitGroup
	for _, info := range windows {
		wg.Add(1)
		go func(info X窗口信息) {
			defer wg.Done()
			mu.Lock()
			defer mu.Unlock()

			// 获取窗口标题长度
			length, _, _ := getWindowTextLengthW.X调用(uintptr(info.X窗口句柄))
			if length == 0 {
				return
			}

			// 分配缓冲区用于存储标题
			buffer := make([]uint16, length+1)
			_, _, _ = getWindowTextW.X调用(uintptr(info.X窗口句柄), uintptr(unsafe.Pointer(&buffer[0])), uintptr(length+1))

			// 获取窗口类名
			classNameBuffer := make([]uint16, 256)
			_, _, _ = getClassNameW.X调用(uintptr(info.X窗口句柄), uintptr(unsafe.Pointer(&classNameBuffer[0])), 256)

			// 获取进程 ID 和线程 ID
			var processID uintptr
			_, _, _ = getWindowThreadProcessId.X调用(uintptr(info.X窗口句柄), uintptr(unsafe.Pointer(&processID)))
			threadID := processID & 0xFFFF // 取低16位作为线程ID

			// 更新窗口信息
			info.X标题 = syscall.UTF16ToString(buffer)
			info.X类名 = syscall.UTF16ToString(classNameBuffer)
			info.X进程id = int(processID)
			info.X线程id = int(threadID)

			// 更新 windows 切片中的信息
			for i, w := range windows {
				if w.X窗口句柄 == info.X窗口句柄 {
					windows[i] = info
					break
				}
			}
		}(info)
	}

	// 等待所有 goroutines 完成
	wg.Wait()

	// 返回所有窗口的信息
	return windows
}

// X枚举子窗口 枚举指定父窗口的直接子窗口（不包括孙窗口等后代窗口）
func X枚举子窗口(窗口句柄 int, 类名筛选 ...string) []int {
	var handles []int

	// 获取第一个子窗口
	hWndChild, _, _ := findWindowExW.X调用(
		uintptr(窗口句柄), // 父窗口句柄
		0,             // 子窗口句柄为0，表示从第一个开始
		0,             // 类名为空
		0)             // 窗口名为空

	// 遍历所有直接子窗口
	for hWndChild != 0 {
		if len(类名筛选) > 0 {
			if X取类名(int(hWndChild)) == 类名筛选[0] {
				handles = append(handles, int(hWndChild))
			}
		} else {
			handles = append(handles, int(hWndChild))
		}

		// 获取下一个兄弟窗口
		hWndChild, _, _ = findWindowExW.X调用(
			uintptr(窗口句柄), // 父窗口句柄
			hWndChild,     // 上一个找到的子窗口句柄
			0,             // 类名为空
			0)             // 窗口名为空
	}

	return handles
}

func X枚举兄弟窗口(窗口句柄 int, 类名筛选 ...string) []int {
	var handles1 []int
	var handles2 []int
	父句柄 := X取父句柄(窗口句柄)
	if 父句柄 != 0 {
		handles1 = X枚举子窗口(父句柄, 类名筛选...)
	}
	for i := range handles1 {
		if handles1[i] != 窗口句柄 {
			handles2 = append(handles2, handles1[i])
		}
	}
	return handles2
}

// X枚举子孙窗口 枚举指定父窗口的所有子窗口。
// 参数:
// 父窗口句柄: 父窗口的句柄。
// 类名筛选: 子窗口类名的字符串。
// 返回值:
// 返回一个包含所有子窗口句柄的切片。如果枚举失败，返回nil。
func X枚举子孙窗口(父窗口句柄 int, 类名筛选 ...string) []int {
	return 内部类2.X枚举子孙窗口(父窗口句柄, 类名筛选...)
}

// X模糊遍历子孙窗口 根据类名和窗口标题模糊匹配父窗口中的子窗口
// 此函数首先枚举父窗口的所有子窗口，然后根据包含的类名和窗口标题进行筛选，返回匹配的子窗口句柄数组
// 参数:
// 父窗口句柄: 父窗口的句柄，用于枚举其子窗口, 如果写0, 则枚举顶级窗口.
// 包含的类名: 类名的子字符串，用于筛选子窗口，为空则不检查类名。
// 包含的窗口标题: 窗口标题的子字符串，用于筛选子窗口，为空则不检查标题。
// 注意: 如果"包含的类名""包含的窗口标题", 两个都不为空, 那么匹配的窗口必须是同时包含这两个字符串。
// 返回值:
// []int: 匹配的子窗口句柄数组
func X模糊遍历子孙窗口(父窗口句柄 int, 包含类名 string, 包含标题 string) []int {
	// 枚举父窗口的所有子窗口
	返回数组 := X枚举子孙窗口(父窗口句柄)
	// 初始化结果数组，用于存储匹配的子窗口句柄
	结果句柄数组 := []int{}
	// 遍历枚举的子窗口数组
	for _, 句柄 := range 返回数组 {
		// 初始化标志，用于判断当前窗口是否符合筛选条件
		是否合法 := true
		// 如果指定了包含的类名，则检查当前窗口的类名是否包含指定的子字符串
		if 包含类名 != "" {
			当前类名 := X取类名(句柄)
			if 文本类.X是否包含(当前类名, 包含类名) == false {
				是否合法 = false
			}
		}

		// 如果指定了包含的窗口标题且当前窗口类名符合要求，则检查当前窗口的标题是否包含指定的子字符串
		if 包含标题 != "" && 是否合法 != false {
			当前标题 := X取标题(句柄)
			if 文本类.X是否包含(当前标题, 包含标题) == false {
				是否合法 = false
			}
		}

		// 如果当前窗口符合所有筛选条件，则将其句柄添加到结果数组中
		if 是否合法 == true {
			结果句柄数组 = append(结果句柄数组, 句柄)
		}
	}

	// 返回匹配的子窗口句柄数组
	return 结果句柄数组
}

// X是否为子孙窗口 判断指定的窗口是否是另一个窗口的子孙窗口。
// 父窗口句柄: 父窗口的句柄。
// 子窗口句柄: 可能是父窗口的子窗口的句柄。
// 返回值: 如果子窗口句柄指定的窗口是父窗口句柄指定窗口的子窗口，则返回true；否则返回false。
func X是否为子孙窗口(父窗口句柄, 子窗口句柄 int) bool {
	return win.IsChild(win.HWND(父窗口句柄), win.HWND(子窗口句柄))
}

// X句柄是否有效 检查给定的窗口句柄是否对应一个有效的窗口。
// 参数: 窗口句柄 - 要检查的窗口句柄。
// 返回值: 如果窗口句柄有效，返回true；否则返回false。
func X句柄是否有效(窗口句柄 int) bool {
	//2025-2-23 备注, 已确定可以用.并且没有bug
	r1, _, _ := isWindow.X调用(uintptr(窗口句柄))

	// 如果返回值r1等于0，表示窗口句柄无效。
	return r1 != 0
}

// X是否置顶 检查指定窗口是否设置为置顶。
// 窗口句柄是标识窗口的整数标识符。
// 返回值表示窗口是否为置顶状态。
func X是否置顶(窗口句柄 int) bool {
	// GWL_EXSTYLE 是一个Windows API常量，用于标识窗口的扩展样式。
	GWL_EXSTYLE := -20
	// WS_EX_TOPMOST 是一个Windows API常量，代表窗口的置顶样式。
	const WS_EX_TOPMOST = 0x8
	// style 用于存储窗口的扩展样式。
	var style int32
	// 调用Windows API函数getWindowLongA，获取窗口的扩展样式。
	// X调用是getWindowLongA函数的内部实现细节，此处使用uintptr进行类型转换以满足API要求。
	r1, _, _ := getWindowLongA.X调用(uintptr(窗口句柄), uintptr(GWL_EXSTYLE))
	// 将获取到的扩展样式转换为int32类型并存储在style变量中。
	style = int32(r1)
	// 检查窗口的扩展样式是否包含WS_EX_TOPMOST，即窗口是否置顶。
	// 如果是，则返回true；否则，返回false。
	return (style & WS_EX_TOPMOST) == WS_EX_TOPMOST
}

// X是否已禁止 判断指定窗口是否已被禁止（禁用）。
// 参数:
// 窗口句柄 - 目标窗口的句柄。
// 返回值:
// 如果窗口被禁止（禁用），则返回 true；否则返回 false。
func X是否已禁止(窗口句柄 int) bool {
	return !win.IsWindowEnabled(win.HWND(窗口句柄))
}

// X取位置和大小 通过窗口句柄获取窗口的位置和大小,返回相对于父窗口的坐标。
// 参数:
// 窗口句柄 - 目标窗口的句柄。
// 返回值:
// X窗口坐标大小结构体，包含窗口的坐标和大小信息。
func X取位置和大小(窗口句柄 int) X窗口坐标大小 {
	// 获取窗口的屏幕坐标
	var rect win.RECT
	win.GetWindowRect(win.HWND(窗口句柄), &rect)

	// 将屏幕坐标转换为父窗口坐标
	parentHwnd := win.GetParent(win.HWND(窗口句柄))
	var topLeft win.POINT
	topLeft.X = rect.Left
	topLeft.Y = rect.Top
	win.ScreenToClient(parentHwnd, &topLeft)

	// 计算宽度和高度
	width := rect.Right - rect.Left
	height := rect.Bottom - rect.Top

	var pt X窗口坐标大小
	pt.X左 = int(topLeft.X)
	pt.X顶 = int(topLeft.Y)
	pt.X宽 = int(width)
	pt.X高 = int(height)
	return pt
}

// X置位置和大小 设置窗口的位置和大小。
// 该函数通过窗口句柄识别窗口，并尝试将其移动到屏幕上的指定位置（由左和顶参数指定），
// 同时调整窗口的宽度和高度（由宽和高参数指定）。
// 如果调整成功，函数返回true；如果失败，则返回false。
// 参数：
// 窗口句柄 - 用于标识窗口的唯一标识符。
// 左 - 窗口左边缘与屏幕左上角的水平距离。
// 顶 - 窗口顶部边缘与屏幕左上角的垂直距离。
// 宽 - 调整后的窗口宽度。
// 高 - 调整后的窗口高度。
// 返回值：
// bool类型，表示窗口位置和大小调整是否成功。
func X置位置和大小(窗口句柄 int, 左, 顶, 宽, 高 int) bool {
	//hwnd: 窗口句柄。
	//x: 窗口左上角的 x 坐标。
	//y: 窗口左上角的 y 坐标。
	//nWidth: 窗口的宽度。
	//nHeight: 窗口的高度。
	//bRepaint: 如果为 TRUE，则窗口将被重绘；如果为 FALSE，则不会重绘。
	return win.MoveWindow(win.HWND(窗口句柄), int32(左), int32(顶), int32(宽), int32(高), true)
}

// X置位置 设置窗口的位置。
//
// 参数:
// 窗口句柄 - 目标窗口的句柄。
// 左 - 窗口左上角的x坐标。
// 顶 - 窗口左上角的y坐标。
//
// 返回值:
// bool - 如果窗口位置设置成功，则返回true；否则返回false。
//
// 说明:
// 本函数通过调用窗口_取位置和大小获取窗口的当前坐标和大小，然后使用moveWindow.X调用设置窗口位置。
// moveWindow.X调用是一个底层系统调用，用于移动窗口到指定的位置。
// 最后，本函数返回一个布尔值以指示窗口位置是否设置成功。
func X置位置(窗口句柄 int, 左, 顶 int) bool {
	// 获取窗口的当前坐标和大小。
	坐标和大小 := X取位置和大小(窗口句柄)
	return X置位置和大小(窗口句柄, 左, 顶, 坐标和大小.X宽, 坐标和大小.X高)
}

// X置大小 调整给定窗口的大小。
// 该函数通过窗口句柄调整窗口的宽度和高度。
// 参数:
// 窗口句柄 - 窗口的唯一标识符。
// 宽 - 窗口的新宽度。
// 高 - 窗口的新高度。
// 返回值:
// bool - 如果调整大小成功，则返回true；否则返回false。
func X置大小(窗口句柄 int, 宽, 高 int) bool {
	// 获取窗口的当前坐标和大小信息。
	坐标和大小 := X取位置和大小(窗口句柄)
	return X置位置和大小(窗口句柄, 坐标和大小.X左, 1, 宽, 高)
}

// X句柄取进程名 通过窗口句柄获取对应的进程名称。
// 参数 窗口句柄: 目标窗口的句柄。
// 返回值: 进程的名称，如果无法获取则返回空字符串。
func X句柄取进程名(窗口句柄 int) string {

	进程id := X句柄取进程ID(窗口句柄)
	// 检查是否成功获取到进程ID
	if 进程id > 0 {
		// 如果获取到有效的进程ID，则通过进程ID获取进程名称
		return 内部类2.ID取进程名(进程id)
	}
	// 如果未获取到进程ID，则返回空字符串
	return ""
}

// X句柄取进程ID 通过窗口句柄获取对应的进程ID。
// 参数 窗口句柄: 目标窗口的句柄。
// 返回值为获取到的进程ID，如果获取失败则返回0。
func X句柄取进程ID(窗口句柄 int) int {
	var 进程id uint32
	win.GetWindowThreadProcessId(win.HWND(窗口句柄), &进程id)
	if 进程id > 0 {
		return int(进程id)
	}
	return 0
}

// X取任务栏句柄 通过调用 findWindowA 函数来获取任务栏的窗口句柄。
// 如果获取成功，则返回任务栏的句柄值；如果获取失败，则返回0。
// 这个函数使用了 DLLValueConversion_go_text_to_LPARAM 函数将 "Shell_TrayWnd" 转换为 LPARAM 类型，
// 作为 findWindowA 函数的第一个参数，第二个参数传入 0。
func X取任务栏句柄() int {
	//2025-2-23 备注,确定可以用, 并且没有bug
	r1, _, _ := findWindowA.X调用(内部类2.DLL_go文本到缓冲区_LPARAM("Shell_TrayWnd"), 0)
	if r1 > 0 {
		return int(r1)
	}
	return 0
}

// X取顶端窗口句柄 返回当前处于最前端的窗口的句柄。
// 该函数通过调用系统函数获取当前激活的窗口句柄，并将其转换为int类型返回。
// 返回值：顶端窗口的句柄，以int类型表示。
func X取顶端窗口句柄() int {
	return int(win.GetForegroundWindow())
}

// X句柄取进程路径 根据给定的窗口句柄获取进程的路径。
// 如果窗口句柄为0，则获取当前进程的路径。
// 参数:
// 窗口句柄 - 窗口的句柄值。
// 返回值:
// 进程的路径字符串，如果无法获取则返回空字符串。
func X句柄取进程路径(窗口句柄 int) string {
	return 进程类.ID取路径(X句柄取进程ID(窗口句柄))
}

// X取鼠标处窗口句柄 返回鼠标光标所在位置的窗口句柄。
// 返回值为鼠标光标所在位置的窗口句柄。
func X取鼠标处窗口句柄() int {
	横坐标, 纵坐标 := X取光标坐标()
	point := win.POINT{
		X: int32(横坐标),
		Y: int32(纵坐标),
	}
	return int(win.WindowFromPoint(point))
}

// X取指定坐标处句柄 是一个函数，用于获取指定屏幕坐标处的窗口句柄。
// 该函数接收两个整型参数：横坐标和纵坐标。
// 返回值为窗口的句柄，类型为整型。
func X取指定坐标处句柄(横坐标, 纵坐标 int) int {
	point := win.POINT{
		X: int32(横坐标),
		Y: int32(纵坐标),
	}
	return int(win.WindowFromPoint(point))
}

// X取矩形 获取窗口的位置和大小信息。
// 参数:
// 窗口句柄: 窗口的句柄。
// 返回值:
// 返回窗口的位置和大小信息。
func X取矩形(窗口句柄 int) X窗口坐标 {
	// 获取窗口的屏幕坐标
	var rect win.RECT
	var pt X窗口坐标
	win.GetWindowRect(win.HWND(窗口句柄), &rect)
	pt.X左 = int(rect.Left)
	pt.X右 = int(rect.Right)
	pt.X顶 = int(rect.Top)
	pt.X下 = int(rect.Bottom)
	return pt
}

// X是否被遮挡 检查指定的窗口是否被其他窗口遮挡。
// 参数:
// 窗口句柄 - 目标窗口的句柄。
// 返回值:
// 如果窗口被遮挡，则返回true；否则返回false。
func X是否被遮挡(窗口句柄 uintptr) bool {
	// 定义窗口样式和属性的常量和结构体
	var GWL_STYLE = -16
	const (
		WS_CHILD    = 0x40000000 // 表示窗口是一个子窗口
		GW_HWNDPREV = 0x00000002 // 获取前一个窗口的句柄
	)

	// RECT结构体描述了一个矩形的左上角和右下角坐标
	type RECT struct {
		Left   int32
		Top    int32
		Right  int32
		Bottom int32
	}

	// WINDOWINFO结构体包含了窗口的详细信息
	type WINDOWINFO struct {
		cbSize          uint32 // 结构体的大小
		rcWindow        RECT   // 窗口矩形
		rcClient        RECT   // 客户区矩形
		dwStyle         uint32 // 窗口样式
		dwExStyle       uint32 // 扩展窗口样式
		dwWindowStatus  uint32 // 窗口状态
		cxWindowBorders uint32 // 窗口边框宽度
		cyWindowBorders uint32 // 窗口边框高度
		atomWindowType  uint16 // 窗口类型
		wCreatorVersion uint16 // 创建窗口的版本
	}

	// 定义局部变量用于存储窗口信息和矩形
	var 局_窗口信息 WINDOWINFO
	var 局_旧矩形 RECT
	var 局_是否子 bool
	var 局_新矩形 RECT
	var 局_旧句柄 uintptr

	// 初始化WINDOWINFO结构体的大小
	局_窗口信息.cbSize = 60

	// 调用GetWindowInfo获取窗口信息
	r1, _, _ := getWindowInfo.X调用(窗口句柄, uintptr(unsafe.Pointer(&局_窗口信息)))
	if r1 != 0 {
		局_旧矩形 = 局_窗口信息.rcWindow
		局_是否子 = WS_CHILD == (局_窗口信息.dwStyle & WS_CHILD)

		// 获取桌面窗口句柄并查找顶层窗口
		r1, _, _ = getDesktopWindow.X调用()
		if 窗口句柄 == r1 {
			r1, _, _ = getTopWindow.X调用(r1)
			r1, _, _ = getWindow.X调用(r1, GW_HWNDPREV)
			窗口句柄 = r1
		}

		// 遍历所有窗口，检查是否有窗口遮挡目标窗口
		for {
			局_旧句柄 = 窗口句柄
			r1, _, _ = getWindow.X调用(窗口句柄, GW_HWNDPREV)
			窗口句柄 = r1
			if 窗口句柄 == 0 {
				break
			}
			r1, _, _ = isWindowVisible.X调用(窗口句柄)
			if r1 != 0 {
				r1, _, _ = getWindowRect.X调用(窗口句柄, uintptr(unsafe.Pointer(&局_新矩形)))
				// 检查窗口是否重叠
				if !(局_新矩形.Right < 局_旧矩形.Left || 局_新矩形.Left > 局_旧矩形.Right || 局_新矩形.Bottom < 局_旧矩形.Top || 局_新矩形.Top > 局_旧矩形.Bottom) {
					return true
				}
			}
			// 处理子窗口
			if 局_是否子 {
				r1, _, _ = getParent.X调用(局_旧句柄)
				窗口句柄 = r1
				r1, _, _ = getWindowLongA.X调用(窗口句柄, uintptr(GWL_STYLE))
				局_是否子 = WS_CHILD == (r1 & WS_CHILD)
			} else {
				break
			}
		}
	}
	return false
}

// X等待出现 等待一个指定的窗口出现。
// 参数:
// 父窗口句柄: 目标窗口的父窗口句柄, 如果为0，则表示只搜索顶级窗口
// 类名: 目标窗口的类名。
// 标题: 目标窗口的标题。
// 超时_秒: 可选参数，指定等待的超时时间（秒）。默认为10秒。
// 返回值:
// 如果在超时时间内找到了指定窗口，则返回窗口句柄。
// 如果超时未找到窗口，则返回-1。
func X等待出现(父窗口句柄 int, 标题, 类名 string, 超时_秒 ...int) int {
	// 设置默认超时时间为10秒
	超时 := 10
	// 如果提供了超时时间，则使用提供的超时时间
	if len(超时_秒) > 0 {
		超时 = 超时_秒[0]
	}
	// 获取当前时间戳作为开始时间
	开始时间戳 := 时间类.X创建并按当前时间().X取秒时间戳()

	// 死循环
	for 超时 > 0 {
		// 每次循环延时300毫秒
		扩展类.X延时(300)
		// 获取当前时间戳
		当前时间戳 := 时间类.X创建并按当前时间().X取秒时间戳()
		// 如果当前时间戳与开始时间戳之差大于超时时间，则退出循环
		if 当前时间戳-开始时间戳 > int64(超时) {
			break
		}
		// 枚举父窗口的所有子孙窗口
		枚举窗口数组 := X枚举子孙窗口(父窗口句柄, 类名)
		// 遍历窗口数组
		for _, 窗口句柄 := range 枚举窗口数组 {
			// 如果窗口句柄有效
			if 窗口句柄 > 0 {
				// 获取窗口标题
				窗口标题 := X取标题(窗口句柄)
				// 如果窗口标题与目标标题相符，则返回窗口句柄
				if 文本类.X删首尾空与字符(窗口标题) == 文本类.X删首尾空与字符(标题) {
					return 窗口句柄
				}
			}
		}
	}
	// 如果超时未找到窗口，则返回-1
	return -1
}

// X更新窗口 刷新指定窗口的显示内容。
// 该函数通过调用底层的 updateWindow 方法，传递窗口句柄来实现窗口的刷新操作。
// 参数:
// 窗口句柄 - 目标窗口的句柄，标识了需要刷新的窗口。
// 返回值:
// bool - 如果窗口刷新成功，则返回 true；否则返回 false。
func X更新窗口(窗口句柄 int) bool {
	return win.UpdateWindow(win.HWND(窗口句柄))
}

func X发送异步消息(窗口句柄, 消息类型, 参数1, 参数2 int) bool {
	结果 := win.PostMessage(win.HWND(窗口句柄), uint32(消息类型), uintptr(参数1), uintptr(参数2))
	return 结果 > 0
}
func X发送消息(窗口句柄, 消息类型, 参数1, 参数2 int) bool {
	结果 := win.SendMessage(win.HWND(窗口句柄), uint32(消息类型), uintptr(参数1), uintptr(参数2))
	return 结果 > 0
}
